"""
多信号集成策略 - 简单加权平均，无机器学习
基于 RSRS、OC_DIFF、RET 三个因子的历史表现动态调整权重
"""
import numpy as np
import pandas as pd
from sklearn.linear_model import LinearRegression
from .base_strategy import BaseStrategy


class MultiSignalEnsembleStrategy(BaseStrategy):
    strategy_name = "multi_signal_ensemble"
    
    def __init__(self, api, symbol_info, market_period):
        super().__init__(api, symbol_info, market_period)
        for symbol in symbol_info.keys():
            # 存储三个因子的历史序列
            self.all_info[symbol]["rsrs_slopes"] = []
            self.all_info[symbol]["corr_series"] = []
            self.all_info[symbol]["ret_series"] = []
            
            # 存储标准化后的因子得分序列
            self.all_info[symbol]["rsrs_score_series"] = []
            self.all_info[symbol]["oc_score_series"] = []
            self.all_info[symbol]["ret_score_series"] = []
            
            # 存储收益率序列
            self.all_info[symbol]["return_series"] = []
            
            # 动态权重（初始等权重）
            self.all_info[symbol]["factor_weights"] = np.array([1.0, 1.0, 1.0])
            self.all_info[symbol]["last_weight_update"] = 0
            
            # 参数
            self.all_info[symbol]["weight_update_interval"] = symbol_info[symbol].get("weight_update_interval", 60)
            self.all_info[symbol]["lookback_for_weight"] = symbol_info[symbol].get("lookback_for_weight", 100)
    
    def calculate_signal(self, symbol, current_pos, kline):
        """计算交易信号 - 基于动态加权的因子得分"""
        # 更新三个因子的得分
        self._update_factor_series(symbol, kline)
        
        # 更新收益率序列
        self._update_return_series(symbol, kline)
        
        # 定期更新因子权重
        self._update_factor_weights(symbol)
        
        # 获取最新的因子得分
        rsrs_scores = self.all_info[symbol]["rsrs_score_series"]
        oc_scores = self.all_info[symbol]["oc_score_series"]
        ret_scores = self.all_info[symbol]["ret_score_series"]
        
        if len(rsrs_scores) == 0 or len(oc_scores) == 0 or len(ret_scores) == 0:
            return 0, 0.0
        
        # 获取当前因子得分
        current_scores = np.array([
            rsrs_scores[-1],
            oc_scores[-1],
            ret_scores[-1]
        ])
        
        # 获取因子权重
        weights = self.all_info[symbol]["factor_weights"]
        
        # 加权平均
        combined_score = np.dot(current_scores, weights) / np.sum(weights)
        
        # 使用 tanh 映射到仓位
        tanh_scale = self.all_info[symbol].get("tanh_scale", 1.0)
        target_position = np.tanh(combined_score * tanh_scale)
        
        return target_position, combined_score
    
    def _update_factor_series(self, symbol, kline):
        """更新三个因子的得分"""
        # 计算 RSRS
        rsrs_score = self._calculate_rsrs(symbol, kline)
        if rsrs_score is not None:
            self.all_info[symbol]["rsrs_score_series"].append(rsrs_score)
            if len(self.all_info[symbol]["rsrs_score_series"]) > 800:
                self.all_info[symbol]["rsrs_score_series"].pop(0)
        
        # 计算 OC
        oc_score = self._calculate_oc_corr(symbol, kline)
        if oc_score is not None:
            self.all_info[symbol]["oc_score_series"].append(oc_score)
            if len(self.all_info[symbol]["oc_score_series"]) > 800:
                self.all_info[symbol]["oc_score_series"].pop(0)
        
        # 计算 RET
        ret_score = self._calculate_ret(symbol, kline)
        if ret_score is not None:
            self.all_info[symbol]["ret_score_series"].append(ret_score)
            if len(self.all_info[symbol]["ret_score_series"]) > 800:
                self.all_info[symbol]["ret_score_series"].pop(0)
    
    def _update_return_series(self, symbol, kline):
        """更新收益率序列"""
        close_prices = kline.close.values
        
        if len(close_prices) < 2:
            return
        
        returns = np.diff(close_prices) / close_prices[:-1]
        current_len = len(returns)
        stored_len = len(self.all_info[symbol]["return_series"])
        
        if stored_len < current_len:
            self.all_info[symbol]["return_series"].extend(returns[stored_len:].tolist())
            if len(self.all_info[symbol]["return_series"]) > 800:
                self.all_info[symbol]["return_series"] = self.all_info[symbol]["return_series"][-800:]
    
    def _update_factor_weights(self, symbol):
        """基于历史表现动态更新因子权重"""
        current_len = len(self.all_info[symbol]["rsrs_score_series"])
        last_update = self.all_info[symbol]["last_weight_update"]
        update_interval = self.all_info[symbol]["weight_update_interval"]
        lookback = self.all_info[symbol]["lookback_for_weight"]
        
        # 检查是否需要更新权重
        if current_len - last_update < update_interval:
            return
        
        # 检查是否有足够的数据
        if current_len < lookback + 1:
            return
        
        rsrs_scores = self.all_info[symbol]["rsrs_score_series"]
        oc_scores = self.all_info[symbol]["oc_score_series"]
        ret_scores = self.all_info[symbol]["ret_score_series"]
        return_series = self.all_info[symbol]["return_series"]
        
        if len(return_series) < lookback + 1:
            return
        
        # 计算每个因子与未来收益率的相关性
        correlations = []
        
        for scores in [rsrs_scores, oc_scores, ret_scores]:
            if len(scores) < lookback + 1:
                correlations.append(0.0)
                continue
            
            # 使用最近 lookback 期的数据
            factor_values = scores[-lookback-1:-1]
            future_returns = return_series[-lookback:]
            
            # 计算相关系数
            if len(factor_values) == len(future_returns) and len(factor_values) > 0:
                corr = np.corrcoef(factor_values, future_returns)[0, 1]
                if np.isnan(corr):
                    corr = 0.0
                correlations.append(abs(corr))  # 使用绝对值，因为负相关也有价值
            else:
                correlations.append(0.0)
        
        # 将相关系数转换为权重（归一化）
        correlations = np.array(correlations)
        if np.sum(correlations) > 0:
            new_weights = correlations / np.sum(correlations) * 3  # 归一化到总和为3
            # 平滑更新：新权重 = 0.7 * 旧权重 + 0.3 * 新权重
            old_weights = self.all_info[symbol]["factor_weights"]
            self.all_info[symbol]["factor_weights"] = 0.7 * old_weights + 0.3 * new_weights
        
        self.all_info[symbol]["last_weight_update"] = current_len
        
        print(f"权重更新 - Bar {current_len}: RSRS={self.all_info[symbol]['factor_weights'][0]:.3f}, "
              f"OC={self.all_info[symbol]['factor_weights'][1]:.3f}, "
              f"RET={self.all_info[symbol]['factor_weights'][2]:.3f}")

    
    # ==================== 因子计算方法 ====================
    
    def _calculate_rsrs(self, symbol, kline):
        """计算 RSRS 因子得分"""
        rsrs_period = self.all_info[symbol].get("rsrs_period", 18)
        rsrs_std_period = self.all_info[symbol].get("rsrs_std_period", 600)
        
        high_prices = kline.high.values
        low_prices = kline.low.values
        rsrs_slopes = self.all_info[symbol]["rsrs_slopes"]
        
        current_len = len(high_prices)
        
        # 批量初始化
        if len(rsrs_slopes) < current_len - rsrs_period:
            start_idx = max(rsrs_period, len(rsrs_slopes))
            for i in range(start_idx, min(current_len, rsrs_std_period + rsrs_period)):
                window_high = high_prices[i-rsrs_period:i]
                window_low = low_prices[i-rsrs_period:i]
                X = window_low.reshape(-1, 1)
                y = window_high
                reg = LinearRegression().fit(X, y)
                rsrs_slopes.append(reg.coef_[0])
        
        # 增量更新
        if len(rsrs_slopes) >= rsrs_std_period:
            window_high = high_prices[-rsrs_period:]
            window_low = low_prices[-rsrs_period:]
            X = window_low.reshape(-1, 1)
            y = window_high
            reg = LinearRegression().fit(X, y)
            slope = reg.coef_[0]
            rsrs_slopes[:-1] = rsrs_slopes[1:]
            rsrs_slopes[-1] = slope
        
        if len(rsrs_slopes) == 0:
            return None
        
        current_slope = rsrs_slopes[-1]
        recent_slopes = rsrs_slopes[-rsrs_std_period:]
        mean_slope = np.mean(recent_slopes)
        std_slope = np.std(recent_slopes)
        
        if std_slope == 0:
            return 0.0
        
        return (current_slope - mean_slope) / std_slope
    
    def _calculate_oc_corr(self, symbol, kline):
        """计算 OC 相关性因子得分"""
        score_period = self.all_info[symbol].get("oc_score_period", 20)
        std_period = self.all_info[symbol].get("oc_std_period", 600)
        
        close_prices = kline.close.values
        open_prices = kline.open.values
        corr_series = self.all_info[symbol]["corr_series"]
        
        current_len = len(close_prices)
        
        # 批量初始化
        if len(corr_series) < current_len - score_period:
            start_idx = max(score_period, len(corr_series))
            for i in range(start_idx, min(current_len, std_period + score_period)):
                window_close = close_prices[i-score_period:i]
                window_open = open_prices[i-score_period:i]
                mem = (window_close - window_open).mean()
                corr = np.corrcoef(window_close, window_open)[0][1] * (1 if mem >= 0 else -1)
                corr_series.append(corr)
        
        # 增量更新
        if len(corr_series) >= std_period:
            window_close = close_prices[-score_period:]
            window_open = open_prices[-score_period:]
            mem = (window_close - window_open).mean()
            corr = np.corrcoef(window_close, window_open)[0][1] * (1 if mem >= 0 else -1)
            corr_series[:-1] = corr_series[1:]
            corr_series[-1] = corr
        
        if len(corr_series) == 0:
            return None
        
        current_score = corr_series[-1]
        recent_score = corr_series[-std_period:]
        mean_score = np.mean(recent_score)
        std_score = np.std(recent_score)
        
        if std_score == 0:
            return 0.0
        
        return (current_score - mean_score) / std_score
    
    def _calculate_ret(self, symbol, kline):
        """计算 RET 因子得分"""
        open_prices = kline.open.values
        high_prices = kline.high.values
        low_prices = kline.low.values
        close_prices = kline.close.values
        
        ret_series = self.all_info[symbol]["ret_series"]
        
        # 批量初始化
        if len(ret_series) == 0:
            ret_series.extend((high_prices + low_prices - open_prices - close_prices).tolist())
        else:
            # 增量更新
            ret_value = high_prices[-1] + low_prices[-1] - open_prices[-1] - close_prices[-1]
            ret_series.append(ret_value)
            if len(ret_series) > 800:
                ret_series.pop(0)
        
        if len(ret_series) == 0:
            return None
        
        # 返回当前值（不做标准化）
        return ret_series[-1]
